/*
 ------------------------------------------------------------------------------
 Copyright (C) 2006-2007 Team Blur.

 This file is part of the Quantum Engine source code.

 The Quantum Engine source code is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or (at your
 option) any later version.

 The Quantum Engine source code is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 more details.

 You should have received a copy of the GNU General Public License along with
 the Quantum Engine source code; if not, write to the Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 ------------------------------------------------------------------------------
*/


#ifndef __MATH_BOUNDS_H__
#define __MATH_BOUNDS_H__


extern odBounds		bounds_zero;

class odBounds {
public:
	odVec3			b[2];

					odBounds (void);
	explicit		odBounds (const odVec3 &mins, const odVec3 &maxs);
	explicit		odBounds (float min, float max);
	explicit		odBounds (float minX, float minY, float minZ, float maxX, float maxY, float maxZ);

	const odVec3 &	operator [] (int index) const;
	odVec3 &		operator [] (int index);

	odBounds &		operator = (const odBounds &bounds);
	bool			operator == (const odBounds &bounds) const;
	bool			operator != (const odBounds &bounds) const;

	odBounds		operator + (const odBounds &bounds) const;
	odBounds		operator + (const odVec3 &vec) const;
	odBounds		operator - (const odBounds &bounds) const;
	odBounds		operator - (const odVec3 &vec) const;
	odBounds &		operator += (const odBounds &bounds);
	odBounds &		operator += (const odVec3 &vec);
	odBounds &		operator -= (const odBounds &bounds);
	odBounds &		operator -= (const odVec3 &vec);

	void			Set (const odVec3 &mins, const odVec3 &maxs);
	void			Set (float min, float max);
	void			Set (float minX, float minY, float minZ, float maxX, float maxY, float maxZ);
	void			Clear (void);
	bool			IsCleared (void) const;
	void			Zero (void);
	bool			IsZeroed (void) const;
	void			AddBounds (const odBounds &bounds);
	void			AddPoint (const odVec3 &point);
	void			Expand (float dist);
	odVec3			GetCenter (void) const;
	float			GetRadius (void) const;
	bool			IntersectsBounds (const odBounds &bounds) const;
	bool			IntersectsSphere (const odVec3 &center, float radius) const;
	bool			IntersectsLine (const odVec3 &start, const odVec3 &end) const;
	bool			ContainsPoint (const odVec3 &point) const;

	void			ToSphere (odVec3 &center, float *radius) const;
	void			FromSphere (const odVec3 &center, float radius);
	void			ToPoints (odVec3 points[8]) const;
	void			FromPoints (const odVec3 points[8]);

	const float *	ToFloatPtr (void) const;
	float *			ToFloatPtr (void);

	const char *	ToString (int precision = 2) const;
};

INLINE odBounds::odBounds (void){

}

INLINE odBounds::odBounds (const odVec3 &mins, const odVec3 &maxs){

	b[0] = mins;
	b[1] = maxs;
}

INLINE odBounds::odBounds (float min, float max){

	b[0].Set(min, min, min);
	b[1].Set(max, max, max);
}

INLINE odBounds::odBounds (float minX, float minY, float minZ, float maxX, float maxY, float maxZ){

	b[0].Set(minX, minY, minZ);
	b[1].Set(maxX, maxY, maxZ);
}

INLINE const odVec3 &odBounds::operator [] (int index) const {

	return b[index];
}

INLINE odVec3 &odBounds::operator [] (int index){

	return b[index];
}

INLINE odBounds &odBounds::operator = (const odBounds &bounds){

	b[0] = bounds[0];
	b[1] = bounds[1];

	return *this;
}

INLINE bool odBounds::operator == (const odBounds &bounds) const {

	if (b[0] != bounds[0] || b[1] != bounds[1])
		return false;

	return true;
}

INLINE bool odBounds::operator != (const odBounds &bounds) const {

	if (b[0] == bounds[0] && b[1] == bounds[1])
		return false;

	return true;
}

INLINE odBounds odBounds::operator + (const odBounds &bounds) const {

	odBounds	result;

	result[0] = b[0];
	result[1] = b[1];

	if (bounds[0][0] < result[0][0])
		result[0][0] = bounds[0][0];
	if (bounds[0][1] < result[0][1])
		result[0][1] = bounds[0][1];
	if (bounds[0][2] < result[0][2])
		result[0][2] = bounds[0][2];

	if (bounds[1][0] > result[1][0])
		result[1][0] = bounds[1][0];
	if (bounds[1][1] > result[1][1])
		result[1][1] = bounds[1][1];
	if (bounds[1][2] > result[1][2])
		result[1][2] = bounds[1][2];

	return result;
}

INLINE odBounds odBounds::operator + (const odVec3 &vec) const {

	return odBounds(b[0] + vec, b[1] + vec);
}

INLINE odBounds odBounds::operator - (const odBounds &bounds) const {

	return odBounds(b[0] + bounds[1], b[1] + bounds[0]);
}

INLINE odBounds odBounds::operator - (const odVec3 &vec) const {

	return odBounds(b[0] - vec, b[1] - vec);
}

INLINE odBounds &odBounds::operator += (const odBounds &bounds){

	if (bounds[0][0] < b[0][0])
		b[0][0] = bounds[0][0];
	if (bounds[0][1] < b[0][1])
		b[0][1] = bounds[0][1];
	if (bounds[0][2] < b[0][2])
		b[0][2] = bounds[0][2];

	if (bounds[1][0] > b[1][0])
		b[1][0] = bounds[1][0];
	if (bounds[1][1] > b[1][1])
		b[1][1] = bounds[1][1];
	if (bounds[1][2] > b[1][2])
		b[1][2] = bounds[1][2];

	return *this;
}

INLINE odBounds &odBounds::operator += (const odVec3 &vec){

	b[0] += vec;
	b[1] += vec;

	return *this;
}

INLINE odBounds &odBounds::operator -= (const odBounds &bounds){

	b[0] += bounds[1];
	b[1] += bounds[0];

	return *this;
}

INLINE odBounds &odBounds::operator -= (const odVec3 &vec){

	b[0] -= vec;
	b[1] -= vec;

	return *this;
}

INLINE void odBounds::Set (const odVec3 &mins, const odVec3 &maxs){

	b[0] = mins;
	b[1] = maxs;
}

INLINE void odBounds::Set (float min, float max){

	b[0].Set(min, min, min);
	b[1].Set(max, max, max);
}

INLINE void odBounds::Set (float minX, float minY, float minZ, float maxX, float maxY, float maxZ){

	b[0].Set(minX, minY, minZ);
	b[1].Set(maxX, maxY, maxZ);
}

INLINE void odBounds::Clear (void){

	b[0][0] = b[0][1] = b[0][2] = M_INFINITY;
	b[1][0] = b[1][1] = b[1][2] = -M_INFINITY;
}

INLINE bool odBounds::IsCleared (void) const {

	if (b[0][0] <= b[1][0] || b[0][1] <= b[1][1] || b[0][2] <= b[1][2])
		return false;

	return true;
}

INLINE void odBounds::Zero (void){

	b[0][0] = b[0][1] = b[0][2] = 0.0f;
	b[1][0] = b[1][1] = b[1][2] = 0.0f;
}

INLINE bool odBounds::IsZeroed (void) const {

	if (b[0][0] != 0.0f || b[0][1] != 0.0f || b[0][2] != 0.0f)
		return false;
	if (b[1][0] != 0.0f || b[1][1] != 0.0f || b[1][2] != 0.0f)
		return false;

	return true;
}

INLINE void odBounds::AddBounds (const odBounds &bounds){

	if (bounds[0][0] < b[0][0])
		b[0][0] = bounds[0][0];
	if (bounds[0][1] < b[0][1])
		b[0][1] = bounds[0][1];
	if (bounds[0][2] < b[0][2])
		b[0][2] = bounds[0][2];

	if (bounds[1][0] > b[1][0])
		b[1][0] = bounds[1][0];
	if (bounds[1][1] > b[1][1])
		b[1][1] = bounds[1][1];
	if (bounds[1][2] > b[1][2])
		b[1][2] = bounds[1][2];
}

INLINE void odBounds::AddPoint (const odVec3 &point){

	if (point[0] < b[0][0])
		b[0][0] = point[0];
	if (point[0] > b[1][0])
		b[1][0] = point[0];

	if (point[1] < b[0][1])
		b[0][1] = point[1];
	if (point[1] > b[1][1])
		b[1][1] = point[1];

	if (point[2] < b[0][2])
		b[0][2] = point[2];
	if (point[2] > b[1][2])
		b[1][2] = point[2];
}

INLINE void odBounds::Expand (float dist){

	b[0][0] -= dist;
	b[0][1] -= dist;
	b[0][2] -= dist;
	b[1][0] += dist;
	b[1][1] += dist;
	b[1][2] += dist;
}

INLINE bool odBounds::IntersectsBounds (const odBounds &bounds) const {

	if (b[0][0] > bounds[1][0] || b[0][1] > bounds[1][1] || b[0][2] > bounds[1][2])
		return false;
	if (b[1][0] < bounds[0][0] || b[1][1] < bounds[0][1] || b[1][2] < bounds[0][2])
		return false;

	return true;
}

INLINE bool odBounds::IntersectsSphere (const odVec3 &center, float radius) const {

	if (b[0][0] > center[0] + radius || b[0][1] > center[1] + radius || b[0][2] > center[2] + radius)
		return false;
	if (b[1][0] < center[0] - radius || b[1][1] < center[1] - radius || b[1][2] < center[2] - radius)
		return false;

	return true;
}

INLINE bool odBounds::IntersectsLine (const odVec3 &start, const odVec3 &end) const {

	odVec3	extents = (b[1] - b[0]) * 0.5f;
	odVec3	dir = (end - start) * 0.5f;
	odVec3	absDir;
	odVec3	vec;

	absDir.Set(M_Fabs(dir[0]), M_Fabs(dir[1]), M_Fabs(dir[2]));

	vec = start + dir - (b[0] + b[1]) * 0.5f;

	if (M_Fabs(vec[0]) > extents[0] + absDir[0])
		return false;
	if (M_Fabs(vec[1]) > extents[1] + absDir[1])
		return false;
	if (M_Fabs(vec[2]) > extents[2] + absDir[2])
		return false;

	if (M_Fabs(dir[1] * vec[2] - dir[2] * vec[1]) > extents[1] * absDir[2] + extents[2] * absDir[1])
		return false;
	if (M_Fabs(dir[2] * vec[0] - dir[0] * vec[2]) > extents[2] * absDir[0] + extents[0] * absDir[2])
		return false;
	if (M_Fabs(dir[0] * vec[1] - dir[1] * vec[0]) > extents[0] * absDir[1] + extents[1] * absDir[0])
		return false;

	return true;
}

INLINE bool odBounds::ContainsPoint (const odVec3 &point) const {

	if (point[0] > b[1][0] || point[1] > b[1][1] || point[2] > b[1][2])
		return false;
	if (point[0] < b[0][0] || point[1] < b[0][1] || point[2] < b[0][2])
		return false;

	return true;
}

INLINE const float *odBounds::ToFloatPtr (void) const {

	return &b[0].x;
}

INLINE float *odBounds::ToFloatPtr (void){

	return &b[0].x;
}


#endif	// __MATH_BOUNDS_H__
